package com.qf.controller;

import com.qf.captcha.CaptchaCodeManager;
import com.qf.pojo.DtsAdmin;
import com.qf.service.DtsPermissionService;
import com.qf.service.RoleService;
import com.qf.util.*;
import com.qf.util.Base64;
import com.qf.util.UUID;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.*;

/**
 * @author Wy
 * @version V1.0
 * @Project MavenDemo
 * @Package com.qf.controller
 * @Description:
 * @Date 2022/6/16 10:40
 */

@RequestMapping("/auth")
//解决前后端跨域访问问题
@CrossOrigin("*")
@Api(value = "权限相关接口，登录，注销，验证码")
public class AuthController {


    //打印日志对象
    private static final Logger logger= LoggerFactory.getLogger(AuthController.class);
    @Autowired
     private   DtsPermissionService dtsPermissionService;
    @Autowired
      private RoleService roleService;
    @Autowired
    private ApplicationContext context;

    private HashMap<String, String> systemPermMap=null;

    @GetMapping("/captchaImage")
    @ApiOperation(value = "获取验证码图片")
    public  Object getCode() throws Exception {
         //1. 生成随机字串
        String code = VerifyCodeUtils.generateVerifyCode(4);
        //2. 验证码唯一标识
        String uuid = UUID.randomUUID().toString(true);
        //3. 验证码存储到内存中
        boolean b = CaptchaCodeManager.addToCache(uuid, code, 10);
        //4. 判断验证码是否存储成功
        if(!b){
            logger.error("生成验证码出错", AdminResponseCode.AUTH_CAPTCHA_FREQUENCY);
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_FREQUENCY);

        }
        //5. 生成验证码图片
        int w=100;
        int h=40;
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        VerifyCodeUtils.outputImage(w,h,byteArrayOutputStream, code);

        //6. 将图片唯一id和图片内容返回
        HashMap<Object, Object> data = new HashMap<>();
        data.put("img", Base64.encode(byteArrayOutputStream.toByteArray()));
        data.put("uuid", uuid);

        return ResponseUtil.ok(data);

    }
    @RequestMapping("/login")
    public Object login(@RequestBody String body){
        //1. 从传入的josn字符串中提取参数数据
        String code = JacksonUtil.parseString(body, "code");

        String password = JacksonUtil.parseString(body, "password");

        String username = JacksonUtil.parseString(body, "username");

        String uuid = JacksonUtil.parseString(body, "uuid");

        //2. 判断参数是否为空, 如果为空返回信息
        if(StringUtils.isEmpty(code)||StringUtils.isEmpty(password)||
                StringUtils.isEmpty(username)
                ||StringUtils.isEmpty(uuid)){
            return  ResponseUtil.badArgument();


        }
        //3. 校验验证码是否正确, 如果验证码不正确返回提示信息, 从新让用户填写
        //从缓存中获取保存的验证码
        String cachedCaptcha = CaptchaCodeManager.getCachedCaptcha(uuid);
        //判断验证码是否超时
        if(cachedCaptcha==null){
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_EXPIRED);
        }
        //对比验证码
        if(!code.equalsIgnoreCase(cachedCaptcha)){
            return AdminResponseUtil.fail(AdminResponseCode.AUTH_CAPTCHA_ERROR);
        }
        //4. 创建shiro的subject对象
        Subject subject = SecurityUtils.getSubject();

        //5. 创建shiro的用户名密码对象, 封装用户页面中输入的用户名, 密码
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);

        //6. 调用登录方法, 传入用户名密码对象, 就会触发shiro的自定义realm进行校验

        try {
            subject.login(usernamePasswordToken);
        } catch (UnknownAccountException e) {
            return  AdminResponseUtil.fail(AdminResponseCode.ADMIN_INVALID_ACCOUNT_OR_PASSWORD);
        }catch (AuthenticationException e){
            return  AdminResponseUtil.fail(AdminResponseCode.ADMIN_INVALID_ACCOUNT_OR_PASSWORD);
        }
        //7. 返回登录成功信息
         return ResponseUtil.ok(subject.getSession().getId());

    }
    /**
     * 获取用户登陆后的详细信息, 用户名, 角色, 权限
     * @return
     */
    @RequestMapping("/info")
     public Object info(){
         //1. 从shiro框架中获取当前登录用户对象
         Subject subject = SecurityUtils.getSubject();
         //2. 获取当前登录用户的用户名和用户头像
         DtsAdmin dtsAdmin = (DtsAdmin) subject.getPrincipal();

         HashMap<String, Object> data = new HashMap<>();
         data.put("name", dtsAdmin.getUsername());
         data.put("avater", dtsAdmin.getAvatar());
         //3. 获取当前用户中的角色id数组
         Integer[] roleIds = dtsAdmin.getRoleIds();
         //4. 根据角色id数组到数据库中查询角色名称集合
         Set<String> roleSet = roleService.queryByIds(roleIds);
         data.put("roles", roleSet);
         //5. 根据角色id数组到数据库中查询权限字符串集合
         Set<String> permSet = dtsPermissionService.queryByRoleIds(roleIds);
         //6. 将权限字符串集合, 转换成前端需要的权限字符串数组
         data.put("perms", toAPI(permSet));
         //返回
         return ResponseUtil.ok(data);

     }
    /**
     * 注销退出
     * @return
     */
    @RequestMapping("/logout")
       public Object logout(){
        Subject subject = SecurityUtils.getSubject();
        subject.logout();

        return  ResponseUtil.ok();
       }





    /**
     * 将权限字符串集合转换成api形式的权限集合
     * @param permSet 例如: admin:brand:list
     * @return
     */
    private Collection<String> toAPI(Set<String> permSet) {
        //封装所有的controller方法, 这些方法应该具有什么样的权限字符串才能够访问
        //systemPermMap中的数据例如: key是admin:brand:list value: POST /admin/brand/list
    if(systemPermMap==null){
      systemPermMap=new HashMap<>();
        //设置我们controller所在的包名
        String basicPackage="com.qf";
        //通过反射机制, 获取指定包中的controller中带自定义权限注解的方法
        List<Permission> permissions = PermissionUtil.listPermission(context, basicPackage);
        //遍历
        for (Permission permission : permissions) {
            //权限字符串数组
            String[] perm = permission.getRequiresPermissions().value();
            //对应的能够访问的url路径
            String api = permission.getApi();
            //key是权限字符串, value是能够访问的url路径
            systemPermMap.put(perm[0],api);
        }
    }
        //封装用来存放当前用户能够访问的url地址
        //例如:  POST /admin/admin/
        HashSet<String> set = new HashSet<>();
    //遍历传入的这个登录用户的权限字符串集合
        for (String perm : permSet) {
            //根据权限字符串, 获取对应的url访问路径
            String api = systemPermMap.get(perm);
              set.add(api);

              if(perm.equals("*")){
                  set.clear();
                  set.add("*");
                  return  set;
              }
        }
        return  set;
    }


}
